home *** CD-ROM | disk | FTP | other *** search
- /* -------------------------------------------------------------------------- **
- * AuCode.c - Amiga uuencode/uudecode toolkit.
- *
- * Written by Christopher R. Hertel; December, 1992
- * email: crh@bubble.mooses.affinity.mn.org
- * -------------------------------------------------------------------------- **
- * Copyright (C) 1993 Christopher R. Hertel
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * -------------------------------------------------------------------------- **
- *
- * $Log: AuCode.c,v $
- * Revision 1.3 94/02/06 14:06:47 CRH
- * With this revision I rewrote the comments for functions Cleanln() and
- * Decodeln(). I fixed some bugs with the checksum calculations (I was
- * getting negative values), and I added an internal function to calculate
- * checksums on bytes triples (au_cdChecksum3()). CRH
- *
- * Revision 1.2 93/09/15 21:27:52 CRH
- * Fixed a small bug in the decoding functions. Plus some general
- * cleanup.
- *
- * Revision 1.1 93/03/25 11:33:21 CRH
- * Cleaned up comments. Clarified License and Copyright.
- *
- * Revision 1.0 93/03/04 00:29:07 CRH
- * Initial revision
- *
- * -------------------------------------------------------------------------- **
- * My understanding of uuencoding/decoding is based on the knowledge that I
- * gained by reading the uuencode source provided with AmigaUUCP V1.16.
- * Many thanks to the authors of that software! The following are comments
- * from their code, which are included as my way of giving credit where
- * credit is due.
- *
- * /begin/
- * [uuencode.c:]
- * Written by Mark Horton
- * Modified by ajr (Alan J Rosenthatl,flaps@utcsri.UUCP) to use checksums
- * Modified by fnf (Fred Fish,well!fnf) to use Keith Pyle's suggestion for
- * compatibility
- * Modified by bcn (Bryce Nesbitt,ucbvax!hoser!bryce) to enable CTRL-C for
- * Amiga Lattice C. Added a transparent file size trailer for later check.
- * Changed fopen from "r" to "rb" for Messy-dos machines (thanks to Andrew
- * Wylie)
- *
- * [uudecode.c:]
- * Modified by bcn (Bryce Nesbitt,ucbvax!cogsci!bryce) to fix a misleading
- * error message on the Amiga port, to fix a bug that prevented decoding
- * certain files, to work even if trailing spaces have been removed from a
- * file, to check the filesize (if present), to add some error checking, to
- * loop for multiple decodes from a single file, and to handle common
- * BITNET mangling. Kludged around a missing string function in Aztec
- * C. Changed "r" to "rb" and "w" to "wb" for Messy-dos machines
- * (Thanks to Andrew Wylie).
- * /end/
- *
- * -------------------------------------------------------------------------- **
- * Initial comments: This module implements the basic uuencode/decode
- * functionality. The functions are designed to be reentrant, so you may
- * use them as part of either a link library or a run-time library.
- * Also, no attempt is made to support any hardware platform other than the
- * Commodore Amiga. In particular, this module is written to compile under
- * SAS C 5.10 or above on an Amiga running OS 2.04 or above. The code is,
- * however, written in a fairly generic style, and should be highly porable.
- *
- * Notes regarding style: I am a Pascal programmer at heart, and most of
- * my style choices are based on my preference for that language and its
- * derivatives. The result is a hybrid format with a few odd quirks of
- * its own. In any case, I intend to produce several modules, so my
- * function, macro, constant, variable, and type naming conventions serve
- * to associate identifiers with modules, and to prevent duplicate names
- * (after all, who else would use such awkward identifiers?).
- *
- * As mentioned above, these modules are designed to work within the
- * multitasking environment of the Commodore Amiga. The code is (should
- * be) reentrant and self contained (I've even avoided ANSI standard
- * functions, because the available libraries are not necessarily
- * reentrant). I have, however, been careful to avoid Amiga-specific
- * functions. It should, therefore, be very easy to port the code to other
- * platforms. (I'm hoping for a microVAX for Christmas. ;-) )
- *
- * Christopher R. Hertel; December, 1992
- * -------------------------------------------------------------------------- **
- */
-
- #include "AuCode.h" /* Header for THIS module. */
-
- /* -------------------------------------------------------------------------- **
- * Macros...
- *
- * Min - This is the classic min() macro. We implement it here to avoid
- * including a header file for some module that we won't ever use.
- */
-
- #define Min(a,b) ((a)<(b))?(a):(b)
-
- /* -------------------------------------------------------------------------- **
- * Functions...
- */
-
- static unsigned int au_cdChecksum3( char *C )
- /* ------------------------------------------------------------------------ **
- * Calculate a checsum over three bytes using au_cdCHKSUMsize as a modulus.
- *
- * Input: C - A pointer to an array of at least three bytes.
- * Output: The checksum of C[0]..C[2], mod au_cdCHKSUMsize.
- * ------------------------------------------------------------------------ **
- */
- {
- unsigned int tmp;
-
- tmp = (unsigned int)C[0];
- tmp += (unsigned int)C[1];
- tmp += (unsigned int)C[2];
- return( tmp % au_cdCHKSUMsize );
- } /* au_cdChecksum3 */
-
- int au_cdCvt3to4( char *S, char *T )
- /* ------------------------------------------------------------------------ **
- * Uuencode a three-byte value. The result is a four-byte value. The
- * function returns the checksum of the three source bytes.
- *
- * Input:
- * S - A pointer to an array of three bytes. These are the source data.
- * T - A pointer to the four byte target array.
- *
- * Output: a checksum value calculated from the three source bytes.
- * ------------------------------------------------------------------------ **
- */
- {
- T[0] = S[0] >> 2;
- T[1] = ((S[0] << 4) & 0x30) | ((S[1] >> 4) & 0x0f);
- T[2] = ((S[1] << 2) & 0x3c) | ((S[2] >> 6) & 0x03);
- T[3] = S[2] & 077;
- T[0] = au_cdENCD( T[0] );
- T[1] = au_cdENCD( T[1] );
- T[2] = au_cdENCD( T[2] );
- T[3] = au_cdENCD( T[3] );
- return( (int)au_cdChecksum3( S ) );
- } /* au_cdCvt3to4 */
-
- int au_cdCvt4to3( char *S, char *T )
- /* ------------------------------------------------------------------------ **
- * Uudecode a four-byte value. The result is a three-byte value. The
- * function returns the checksum of the three target bytes.
- *
- * Input:
- * S - A pointer to an array of four bytes. These are the source data.
- * T - A pointer to the three byte target array.
- *
- * Output: a checksum value calculated from the three target bytes.
- * ------------------------------------------------------------------------ **
- */
- {
- T[0] = (au_cdDECD(S[0]) << 2) | (0x03 & (au_cdDECD(S[1]) >> 4));
- T[1] = (au_cdDECD(S[1]) << 4) | (0x0f & (au_cdDECD(S[2]) >> 2));
- T[2] = (au_cdDECD(S[2]) << 6) | ( 077 & (au_cdDECD(S[3])) );
- return( (int)au_cdChecksum3( T ) );
- } /* au_cdCvt4to3 */
-
- int au_cdEncode( char *sBufr, int sbSize, char *tBufr, int tbSize )
- /* ------------------------------------------------------------------------ **
- * Encode a block of characters using the UUENCODE coding scheme.
- *
- * Input:
- * sBufr - A pointer to a buffer containing the bytes to be encoded.
- * sbSize - The number of bytes in sBufr to be encoded.
- * tBufr - A pointer to the target buffer.
- * tbSize - The number of bytes available in tBufr.
- *
- * Output: The number of source bytes that were actually converted. If
- * this number is less than sbSize, an error occured during the
- * encoding process (ran out of room in tBufr). The best way to
- * recover is to write out the contents of tBufr (which, if they
- * exist, are valid) and call this function again, passing in a
- * pointer to the remainder of the source bytes.
- * -> However, if zero bytes were processed (i.e., this function
- * returns zero), then tBufr is simply not large enough.
- *
- * Note: This function will not write the terminating line.
- * ------------------------------------------------------------------------ **
- */
- {
- int CharCount = 0;
- int TargPos = 0;
- int PhraseLen;
- int TriplLen;
- int PhrasePos;
- char *sp;
- int CheckSum;
-
- tBufr[0] = '\0'; /* Mark the end of the empty target string. */
- while( CharCount < sbSize )
- {
- CheckSum = 0;
- /* Calculate length of next input phrase.
- * We need output space to store the translated phrase plus a nul byte.
- * If there's not enough room, return now.
- */
- PhraseLen = (sbSize - CharCount); /* Bytes left in sBufr */
- PhraseLen = Min( au_cdMAXPhrase, PhraseLen );
- if( (au_cdExpPhrLen(PhraseLen)) >= (tbSize - TargPos) )
- return( CharCount );
-
- /* First convert as many whole triples as possible. This is fairly
- * quick. TriplLen is the greatest multiple of three that is
- * <= PhraseLen. sp is the "starting point", it points to the
- * first char of the phrase.
- */
- TriplLen = 3 *(PhraseLen / 3);
- sp = &sBufr[CharCount];
- tBufr[TargPos++] = au_cdENCD( (unsigned char)PhraseLen );/* phrase length */
- for( PhrasePos = 0; (PhrasePos < TriplLen); PhrasePos += 3, TargPos += 4 )
- CheckSum = ( (CheckSum + au_cdCvt3to4(&sp[PhrasePos], &tBufr[TargPos]))
- % au_cdCHKSUMsize );
- /* Now get any odd bytes at the end of the phrase. */
- TriplLen = (PhraseLen - TriplLen);
- if( TriplLen )
- {
- char tmp[] = {0, 0, 0};
- int i; /* Position within tmp. */
-
- for( i = 0; (i < TriplLen); i++, PhrasePos++ )
- tmp[i] = sp[PhrasePos];
- CheckSum = ( (CheckSum + au_cdCvt3to4( tmp, &tBufr[TargPos] ))
- % au_cdCHKSUMsize );
- TargPos += 4;
- }
- tBufr[TargPos++] = au_cdENCD(CheckSum); /* Write the checksum. */
- tBufr[TargPos++] = '\n'; /* Write a newline. */
- tBufr[TargPos] = '\0'; /* Terminate the buffer.*/
- CharCount += PhraseLen; /* *Now* advance the character count. */
- }
- return( CharCount ); /* Succesfully completed the entire block. */
- } /* au_Encode */
-
- int au_cdCleanln( char *sBufr, int sbSize, char *tBufr, int tbSize )
- /* ------------------------------------------------------------------------ **
- * Clean a line of input to ensure that it can be decoded properly.
- *
- * Input: sBufr - A pointer to an array of encoded bytes. The
- * contents of sBufr[] (the "source") are assumed to
- * be a single line of uuencoded text. The line is
- * considered to be terminated by ascii value less
- * than 32.
- *
- * sbSize - The number of significant bytes in sBufr[]. That
- * is, the maximum number of bytes that you wish to
- * have cleaned. Cleanln() will process no more than
- * min( tbSize, sbSize ) bytes.
- *
- * tBufr - A pointer to the target buffer. The target buffer
- * will receive the "cleaned" copy of the source line.
- * Note that, in general, tBufr should be at least
- * sbSize bytes long, and possibly longer. It should
- * always be safe to create a tBufr that is
- * au_cdMAX_LINE bytes long (unless you are using in
- * this module in some new and interesting way that is
- * beyond my ability to extrapolate).
- *
- * tbSize - The number of bytes available in tBufr. Cleanln()
- * will process no more than min( tbSize, sbSize )
- * bytes.
- *
- * Output: The number of characters in sBufr[] that were copied to
- * tBufr[]. This value does not include any spaces that were
- * added to the end of tBufr[] as padding.
- *
- * Notes: The input line is "cleaned" as follows:
- *
- * This function copies the input line to a new buffer. During
- * the copy the function repairs damage that *may* have occurred
- * during transmission.
- *
- * In particular, some UUENCODErs encode a nul sixel as a space
- * (this module encodes a nul as a "`" character, both decode
- * to the same thing). Some editors (and other text utilites)
- * will trim trailing spaces from the ends of lines. This
- * function pads the end of the target buffer with spaces to
- * avoid the problem of lost sixles.
- *
- * Another potential error exists because BITNET converts the
- * "^" to a "~". Technically, the "~" can't be part of the code,
- * so it's safe to convert it back using the au_cdCleanSixel()
- * macro.
- *
- * This function always places a nul byte in the last position
- * in tBufr[]. That is:
- * tBufr[tbSize-1] = '\0';
- * be certain that your tBufr[] is big enough to handle all the
- * valid characters in sBufr[] plus the nul!
- * ------------------------------------------------------------------------ **
- */
- {
- register int i;
- register int maxMove;
- int tmp;
-
- maxMove = Min( tbSize, sbSize ); /* Max # of bytes to *copy* to tBufr[]. */
- for( i = 0; (i < maxMove) && (32 <= sBufr[i]); i++ )
- {
- tBufr[i] = au_cdCleanSixel(sBufr[i]); /* Clean and copy chars to target. */
- }
- tmp = i+1;
-
- for( ; (i < tbSize); i++ ) /* Pad the end of the line with spaces. */
- {
- tBufr[i] = ' '; /* Pad remainder of tBufr with spaces. */
- }
- tBufr[tbSize-1] = '\0'; /* Terminate tBufr. */
- return( tmp );
- } /* au_cdCleanln */
-
- int au_cdDecodeln( char *sBufr, char *tBufr, int tbSize, int *ErrCD )
- /* ------------------------------------------------------------------------ **
- * This function will decode one "line" of encoded input.
- *
- * Input: sBufr - A pointer to a buffer containing the encoded data
- * that is to be decoded. The data should be in the
- * form of a "line" of uuencoded text. The line is
- * assumed to be "clean" (see au_cdCleanln()).
- * tBufr - A pointer to the target buffer. tBufr[] will
- * receive the decoded data.
- * tbSize - Number of bytes available in tBufr[].
- * ErrCD - A pointer to an integer that will receive an
- * error code. Check *ErrCD for one of the following
- * values:
- *
- * au_cdErr_NoError - No error. If the function
- * return value is zero *and* the error code is
- * _NoError, then an empty (i.e., terminating) line
- * has been found, indicating end of the (encoded)
- * file.
- *
- * au_cdErr_Cramped - tBufr[] does not contain enough
- * space to receive the decoded information. A
- * single encoded line may contain, at most, 63
- * encoded (source) bytes.
- *
- * au_cdErr_Checksum - the checksum comparison failed.
- * Either the source line is mangled, or the
- * checksum was not written properly by the encoding
- * program.
- *
- * Output: The number of bytes that were generated as a result of
- * decoding the input line.
- *
- * Notes: sBufr[] is assumed to be "clean". See the notes associated
- * with function au_cdCleanln() for an explanation of the term "clean",
- * as used in this context.
- * ------------------------------------------------------------------------ **
- */
- {
- register int i;
- int decodeLen = au_cdDECD(*sBufr);
- int Quads,
- QuadSize;
- int sPos, tPos;
- int CheckSum = 0;
-
- *ErrCD = au_cdErr_NoError; /* initialize the error return value */
-
- /* The original data is stored in four-byte chunks. The variable <Quads>
- * represents the number of four-byte chunks required to store
- * <decodeLen> original bytes. <QuadSize> is the number of bytes that
- * are required in order to decode all of the four-byte chunks. Note
- * that QuadSize may be greater than decodeLen by 1 or 2 bytes. This is
- * because we decode the line one chunk at a time, generating three bytes
- * at a time. One or two of those bytes may be padding.
- */
- Quads = ( ( decodeLen + 2 ) / 3 );
- QuadSize = ( Quads * 3 );
-
- /* If tBufr doesn't have room for the decoded bytes, return zero now.
- */
- if( tbSize < QuadSize )
- {
- *ErrCD = au_cdErr_Cramped;
- return( 0 );
- }
-
- /* Now decode all of the quads. Note that sPos is set to 1. This is
- * because the first character in sBufr (i.e., sBufr[0]) is the length
- * byte, and not actually part of the encoded data.
- */
- for( i = 0, sPos = 1, tPos = 0; (i < Quads); i++, sPos += 4, tPos += 3 )
- {
- CheckSum = ( (CheckSum + au_cdCvt4to3( &sBufr[sPos], &tBufr[tPos] ))
- % au_cdCHKSUMsize );
- }
-
- /* Check the checksum. */
- if( CheckSum != (int)(au_cdDECD(sBufr[sPos])) )
- *ErrCD = au_cdErr_Checksum; /* If checksum bad, set errorcode,*/
-
- return( decodeLen );
- } /* au_cdDecodeln */
-
- /* ========================================================================== */
-